﻿
namespace uTorrentJunctions
{
    using System;
    using System.Collections.Generic;
    //using System.Collections.Concurrent;
    using System.Windows.Forms;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.IO;
    using Bencode;
    using JunctionPoints;
    using System.Diagnostics;

    /// <summary>
    /// Main class holding data and processing junctions
    /// </summary>
    public class uTorrentJunctions
    {
        public uTorrentJunctions()
        {
        }
#region Members
        private Dictionary<String, uTorrentClass.uTorrentItem> uTorrentItems; // holds data
        public List<String> selectedLabels = new List<String>(); // Holds user selection
        public List<String> skippedTrackers = new List<String>(); // Holds user selection
        private String _resumeLocation = ""; // Path of resume.dat or resume_dir
        private String _junctionsPath = ""; // Path of Junctions
        public bool ignoreIncomplete = true;
        public enum ResumeType { InResumeDat, InResumeDir }; // Future usage
        public ResumeType resumeType;
        public Queue<String> log = new Queue<string>();
#endregion

#region Properties
        public String resumeLocation
        {
            get { return _resumeLocation; }
            set { _resumeLocation = value.TrimEnd('\\').TrimEnd('/'); }
        }
        public String junctionsPath
        {
            get { return _junctionsPath; }
            set { _junctionsPath = value.TrimEnd('\\').TrimEnd('/'); }
        }
#endregion

#region PrivateMethods
        private void LogAdd(String logged)
        {
            log.Enqueue("[" + DateTime.Now.ToString("hh:mm") + "] " + logged);
        }
#endregion

#region PublicMethods
        public bool IsReady()
        {
            // Considered ready when both junctionPath and resumeLocation exists, and there is at least one selectedLabel
            if (Directory.Exists(junctionsPath) 
                && (this.resumeType == ResumeType.InResumeDat && File.Exists(resumeLocation)
                    || this.resumeType == ResumeType.InResumeDir && Directory.Exists(resumeLocation))
                && selectedLabels.Count > 0)
            {
                return true;
            }
            else
            {
                return false;
            }

        }
        public bool DecodeResumeDat()
        {
            switch(resumeType)
            {
                case ResumeType.InResumeDat:
                    Object decoded = null;
                    MemoryStream memoryStream = new MemoryStream();

                    // Copy file to memory
                    try
                    {
                        using (FileStream fs = new FileStream(resumeLocation, FileMode.Open))
                        {
                            fs.CopyTo(memoryStream);
                        }
                    }
                    catch (Exception ex)
                    {
                        LogAdd("Error opening " + resumeLocation + " (" + ex.Message + ")");
                        return false;
                    }

                    // Decode content
                    decoded = BencodeClass.Decode(memoryStream);

                    // Update dictionary
                    try
                    {
                        this.uTorrentItems = uTorrentClass.GetuTorrentItems(decoded);
                    }
                    catch (Exception ex)
                    {
                        LogAdd("Error reading data from " + resumeLocation + " (" + ex.Message + ")");
                        return false;
                    }
                    LogAdd("Loaded resume.dat.");
                    return true;
                case ResumeType.InResumeDir:
                    // To implement
                    return false;
                default:
                    return false;
            }
        }
        public void UpdateJunctions()
        {
            // Create and remove any junctions according to label and tracker filters
            if (!this.IsReady()) { return; }

            // Get list of matching uTorrentItems
            // NOTE: Only the primary label is considered.
            List<uTorrentClass.uTorrentItem> filteredItems = (from i in uTorrentItems.Values
                                                              where !(!i.completed & ignoreIncomplete)
                                                              && !i.isFile
                                                              && selectedLabels.Contains(i.label)
                                                              && !skippedTrackers.Contains(i.trackerdomain)
                                                              select i).ToList();

            // Build a list of junctions that should be present
            // junctionsToAdd(Path to put junctions, Target path of junctions)
            Dictionary<String, String> junctionsToAdd = new Dictionary<String, String>();
            foreach (uTorrentClass.uTorrentItem i in filteredItems)
            {
                String JunctionPath = junctionsPath + '\\' + i.pathpart;
                if (!junctionsToAdd.ContainsKey(JunctionPath)) // Don't add twice (eg. several torrents sharing the same download folder)
                {
                    String JunctionTarget = uTorrentItems[i.key].path;
                    junctionsToAdd.Add(JunctionPath, JunctionTarget);
                }
            }

            // Get a list of existing folders/junctions
            List<String> foldersExisting = Directory.GetDirectories(junctionsPath).ToList();

            // Build list of junctions that already exist, and orphaned junctions to remove
            List<String> junctionsExist = new List<String>();
            foreach (KeyValuePair<String, String> j in junctionsToAdd)
            {
                if (foldersExisting.Contains(j.Key))
                {
                    junctionsExist.Add(j.Key);
                    foldersExisting.Remove(j.Key);
                }
            }
            // Remove existing junctions from list
            foreach (String s in junctionsExist)
            {
                junctionsToAdd.Remove(s);
            }

            // Create new junctions
            foreach (KeyValuePair<String, String> j in junctionsToAdd)
            {
                if (Directory.Exists(j.Value))
                {
                    JunctionPoint.Create(j.Key, j.Value, false);
                    LogAdd("Junction created: " + j.Key.Replace(junctionsPath, ""));
                }
            }

            // Delete orphaned junctions
            foreach (String s in foldersExisting)
            {
                if (JunctionPoint.Exists(s))
                {
                    JunctionPoint.Delete(s);
                    LogAdd("Junction removed: " + s.Replace(junctionsPath, ""));
                }
            }
        }
        public Dictionary<String, int> GetLabels()
        {
            // Return labels (key) and item count (value)
            // Only the primary label is used.
            return (from uTorrentClass.uTorrentItem u in uTorrentItems.Values
                    where !(!u.completed && ignoreIncomplete)
                    && !u.isFile
                    group u.label by u.label into g
                    select g).ToDictionary(g => g.Key, g => g.Count());
        }
        public Dictionary<String, int> GetTrackers() 
        {
            // Return trackers (key) and item count (value)
            return (from uTorrentClass.uTorrentItem u in uTorrentItems.Values
                    where !(!u.completed && ignoreIncomplete)
                    && !u.isFile
                    && selectedLabels.Contains(u.label)
                    group u.trackerdomain by u.trackerdomain into g
                    select g).OrderBy(g => g.Key).ToDictionary(g => g.Key, g => g.Count());
        }
        public List<String> GetTorrents() 
        {
            // Return torrents
            return (from uTorrentClass.uTorrentItem u in uTorrentItems.Values
                    where !(!u.completed && ignoreIncomplete)
                            && !u.isFile
                            && selectedLabels.Contains(u.label)
                            && !skippedTrackers.Contains(u.trackerdomain)
                    select u.caption).OrderBy(i => i).ToList();
        }
#endregion
    }
}
